﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Snake
{
    class Game
    {
        private Random rnd;
        private Drawer drawer;
        private List<Node> nodes;
        private Node targetNode;
        private KeyboardHendler kh;
        private int winCounts;
        private int snakeSpeed;

        public void StartGame(int borderWidth, int borderHeight, int startX, int startY, char borderSymbol, ConsoleColor borderColor, 
                              char nodeSymbol, ConsoleColor nodeColor, char targetNodeSymbol, ConsoleColor targetNodeColor)
        {
            Console.CursorVisible = false;
            rnd = new Random();
            drawer = new Drawer();
            nodes = new List<Node>();
            kh = new KeyboardHendler();
            snakeSpeed = 500;
            kh.KeyRight += MoveRight;
            kh.KeyLeft += MoveLeft;
            kh.KeyUp += MoveUp;
            kh.KeyDown += MoveDown;

            nodes.Add(new Node(rnd.Next(startX + 1, startX + borderWidth - 2), rnd.Next(startY + 1, startY + borderHeight - 2), nodeColor, nodeSymbol));

            targetNode = GenerateRandomTargetNode(borderWidth, borderHeight, startX, startY, targetNodeSymbol, targetNodeColor);
            drawer.DrawNode(targetNode);

            bool isGameOver = false;
            Console.ReadKey();
            do
            {
                Console.Clear();
                bool isNewRound = false;
                do
                {
                    Console.SetCursorPosition(0, 0);
                    drawer.DrawBorder(borderWidth, borderHeight, startX, startY, borderSymbol, borderColor);
                    drawer.DrawNodeList(nodes);
                    drawer.DrawNode(targetNode);
                    drawer.DrawScore(5, winCounts);
                    kh.QueryKB();
                    nodes[0].Move(CheckPosition);
                    MoveSnakeUnit(nodes);

                    Thread.Sleep(snakeSpeed);

                    if (IsHeadOnTarget(targetNode.X, targetNode.Y, nodes) == true)
                    {
                        winCounts++;
                        snakeSpeed -= 10;

                        drawer.ClearNode(targetNode);
                        isNewRound = true;
                    }

                    if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) || IsHeadOnBody(nodes[0].X, nodes[0].Y, nodes))
                    {
                        isGameOver = true;
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.SetCursorPosition(startX + borderWidth + 5, startY + 2);
                        Console.Write("Game Over");
                        Console.ReadKey();
                        break;
                    }

                } while (!isNewRound);

                targetNode = GenerateRandomTargetNode(borderWidth, borderHeight, startX, startY, targetNodeSymbol, targetNodeColor);
                drawer.DrawNode(targetNode);

            } while (!isGameOver);
        }

        public void MoveSnakeUnit(List<Node> nodes)
        {
            for (int i = nodes.Count - 1; i >= 1; i--)
            {
                nodes[i].X = nodes[i - 1].X;
                nodes[i].Y = nodes[i - 1].Y;
            }
        }

        private Node GenerateRandomTargetNode(int borderWidth, int borderHeight, int startX, int startY, char targetNodeSymbol, ConsoleColor targetNodeColor)
        {
            return new Node(rnd.Next(startX + 1, startX + borderWidth - 2), rnd.Next(startY + 1, startY + borderHeight - 2), targetNodeColor, targetNodeSymbol);
        }

        public bool IsHeadOnTarget(int x, int y, List<Node> nodes)
        {
            foreach (var node in nodes)
                if (x == node.X && y == node.Y)
                    return true;
            return false;
        }

        public bool IsHeadOnBody(int x, int y, List<Node> nodes)
        {
            for (int i = 1; i < nodes.Count; i++)
            {
                if (x == nodes[i].X && y == nodes[i].Y)
                    return true;
            }
            return false;
        }

        public bool IsHeadOnBorder(int x, int y, Drawer drawer)
        {
            if ((x == drawer.X + drawer.width - 1) || (x == drawer.X) || (y == drawer.Y + drawer.height - 1) || (y == drawer.Y))
                return true;
            return false;
        }

        public void MoveRight(object sender, EventArgs e)
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].direction = Direction.Right;
            }
        }

        public void MoveLeft(object sender, EventArgs e)
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].direction = Direction.Left;
            }
        }

        public void MoveUp(object sender, EventArgs e)
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].direction = Direction.Up;
            }
        }

        public void MoveDown(object sender, EventArgs e)
        {
            if (IsHeadOnBorder(nodes[0].X, nodes[0].Y, drawer) == false)
            {
                nodes[0].direction = Direction.Down;
            }
        }

        public Position CheckPosition(int x, int y)
        {
            if (IsHeadOnBody(x, y, nodes))
                return Position.Node;
            if (IsHeadOnBorder(x, y, drawer))
                return Position.Border;
            return Position.Free;
        }
    }

    enum Position
    {
        Node,
        Border,
        Free
    }
}
